/*
 * Copyright (c) 2008, 2016, OneAPM and/or its affiliates. All rights reserved.
 */
package me.yufan.chess.ui.animations;

/**
 * Title: AnimationEquation
 * Description: Easing functions for animations.
 *
 * @author Yufan
 * @version 1.0.0
 * @since 2016-01-26 22:55
 */
public enum AnimationEquation {
    /* Linear */
    LINEAR {
        @Override
        public float calc(float t) {
            return t;
        }
    },

    /* Quadratic */
    IN_QUAD {
        @Override
        public float calc(float t) {
            return t * t;
        }
    },
    OUT_QUAD {
        @Override
        public float calc(float t) {
            return -1 * t * (t - 2);
        }
    },
    IN_OUT_QUAD {
        @Override
        public float calc(float t) {
            t = t * 2;
            if (t < 1)
                return 0.5f * t * t;
            t = t - 1;
            return -0.5f * (t * (t - 2) - 1);
        }
    },

    /* Cubic */
    IN_CUBIC {
        @Override
        public float calc(float t) {
            return t * t * t;
        }
    },
    OUT_CUBIC {
        @Override
        public float calc(float t) {
            t = t - 1;
            return t * t * t + 1;
        }
    },
    IN_OUT_CUBIC {
        @Override
        public float calc(float t) {
            t = t * 2;
            if (t < 1)
                return 0.5f * t * t * t;
            t = t - 2;
            return 0.5f * (t * t * t + 2);
        }
    },

    /* Quartic */
    IN_QUART {
        @Override
        public float calc(float t) {
            return t * t * t * t;
        }
    },
    OUT_QUART {
        @Override
        public float calc(float t) {
            t = t - 1;
            return -1 * (t * t * t * t - 1);
        }
    },
    IN_OUT_QUART {
        @Override
        public float calc(float t) {
            t = t * 2;
            if (t < 1)
                return 0.5f * t * t * t * t;
            t = t - 2;
            return -0.5f * (t * t * t * t - 2);
        }
    },

    /* Quintic */
    IN_QUINT {
        @Override
        public float calc(float t) {
            return t * t * t * t * t;
        }
    },
    OUT_QUINT {
        @Override
        public float calc(float t) {
            t = t - 1;
            return (t * t * t * t * t + 1);
        }
    },
    IN_OUT_QUINT {
        @Override
        public float calc(float t) {
            t = t * 2;
            if (t < 1)
                return 0.5f * t * t * t * t * t;
            t = t - 2;
            return 0.5f * (t * t * t * t * t + 2);
        }
    },

    /* Sine */
    IN_SINE {
        @Override
        public float calc(float t) {
            return -1 * (float) Math.cos(t * (Math.PI / 2)) + 1;
        }
    },
    OUT_SINE {
        @Override
        public float calc(float t) {
            return (float) Math.sin(t * (Math.PI / 2));
        }
    },
    IN_OUT_SINE {
        @Override
        public float calc(float t) {
            return (float) (Math.cos(Math.PI * t) - 1) / -2;
        }
    },

    /* Exponential */
    IN_EXPO {
        @Override
        public float calc(float t) {
            return (t == 0) ? 0 : (float) Math.pow(2, 10 * (t - 1));
        }
    },
    OUT_EXPO {
        @Override
        public float calc(float t) {
            return (t == 1) ? 1 : (float) -Math.pow(2, -10 * t) + 1;
        }
    },
    IN_OUT_EXPO {
        @Override
        public float calc(float t) {
            if (t == 0 || t == 1)
                return t;
            t = t * 2;
            if (t < 1)
                return 0.5f * (float) Math.pow(2, 10 * (t - 1));
            t = t - 1;
            return 0.5f * ((float) -Math.pow(2, -10 * t) + 2);
        }
    },

    /* Circular */
    IN_CIRC {
        @Override
        public float calc(float t) {
            return -1 * ((float) Math.sqrt(1 - t * t) - 1);
        }
    },
    OUT_CIRC {
        @Override
        public float calc(float t) {
            t = t - 1;
            return (float) Math.sqrt(1 - t * t);
        }
    },
    IN_OUT_CIRC {
        @Override
        public float calc(float t) {
            t = t * 2;
            if (t < 1)
                return -0.5f * ((float) Math.sqrt(1 - t * t) - 1);
            t = t - 2;
            return 0.5f * ((float) Math.sqrt(1 - t * t) + 1);
        }
    },

    /* Back */
    IN_BACK {
        @Override
        public float calc(float t) {
            return t * t * ((OVERSHOOT + 1) * t - OVERSHOOT);
        }
    },
    OUT_BACK {
        @Override
        public float calc(float t) {
            t = t - 1;
            return t * t * ((OVERSHOOT + 1) * t + OVERSHOOT) + 1;
        }
    },
    IN_OUT_BACK {
        @Override
        public float calc(float t) {
            float overshoot = OVERSHOOT * 1.525f;
            t = t * 2;
            if (t < 1)
                return 0.5f * (t * t * ((overshoot + 1) * t - overshoot));
            t = t - 2;
            return 0.5f * (t * t * ((overshoot + 1) * t + overshoot) + 2);
        }
    },

    /* Bounce */
    IN_BOUNCE {
        @Override
        public float calc(float t) {
            return 1 - OUT_BOUNCE.calc(1 - t);
        }
    },
    OUT_BOUNCE {
        @Override
        public float calc(float t) {
            if (t < 0.36363636f)
                return 7.5625f * t * t;
            else if (t < 0.72727273f) {
                t = t - 0.54545454f;
                return 7.5625f * t * t + 0.75f;
            } else if (t < 0.90909091f) {
                t = t - 0.81818182f;
                return 7.5625f * t * t + 0.9375f;
            } else {
                t = t - 0.95454546f;
                return 7.5625f * t * t + 0.984375f;
            }
        }
    },
    IN_OUT_BOUNCE {
        @Override
        public float calc(float t) {
            if (t < 0.5f)
                return IN_BOUNCE.calc(t * 2) * 0.5f;
            return OUT_BOUNCE.calc(t * 2 - 1) * 0.5f + 0.5f;
        }
    },

    /* Elastic */
    IN_ELASTIC {
        @Override
        public float calc(float t) {
            if (t == 0 || t == 1)
                return t;
            float period = 0.3f;
            t = t - 1;
            return -((float) Math.pow(2, 10 * t) * (float) Math
                .sin(((t - period / 4) * (Math.PI * 2)) / period));
        }
    },
    OUT_ELASTIC {
        @Override
        public float calc(float t) {
            if (t == 0 || t == 1)
                return t;
            float period = 0.3f;
            return (float) Math.pow(2, -10 * t) * (float) Math
                .sin((t - period / 4) * (Math.PI * 2) / period) + 1;
        }
    },
    IN_OUT_ELASTIC {
        @Override
        public float calc(float t) {
            if (t == 0 || t == 1)
                return t;
            float period = 0.44999996f;
            t = t * 2 - 1;
            if (t < 0)
                return -0.5f * ((float) Math.pow(2, 10 * t) * (float) Math
                    .sin((t - period / 4) * (Math.PI * 2) / period));
            return (float) Math.pow(2, -10 * t) * (float) Math
                .sin((t - period / 4) * (Math.PI * 2) / period) * 0.5f + 1;
        }
    };

    /**
     * Overshoot constant for "back" easing.
     */
    private static final float OVERSHOOT = 1.70158f;

    /**
     * Calculates a new {@code t} value using the animation equation.
     *
     * @param t the raw {@code t} value [0,1]
     * @return the new {@code t} value [0,1]
     */
    public abstract float calc(float t);
}
